#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <config.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
#include <display_manager.h>

static int g_fbDispaly;
static int g_fbSzie;
static unsigned char *g_pfbmmap;
static int fbDevInit(void);
static int fbDevExit(void);
static unsigned char * getFbDisplayMmap(void);
static int fbShowPixel(int x, int y, unsigned int color);
static int fbClear(unsigned int color);
static int fbClearArea(int xStart,int yStart, int xEnd, int yEnd, unsigned int color);
static int fbDrawLine(int xStart,int yStart, int lenth, int blod, unsigned int color);

static T_displayOpr g_tDisplayOpr = 
{
	.name 					= "fb",
	.displayDeviceInit 		= fbDevInit,
	.displayDeviceExit 		= fbDevExit,
	.displayShowPixel 		= fbShowPixel,
	.displayDeviceClear 	= fbClear,
	.displayDeviceClearArea = fbClearArea,
	.displayDrawLine		= fbDrawLine,
	.getDisplayMmap     	= getFbDisplayMmap,
	.next 					= NULL,
};

static int fbDevInit(void)
{
	int ret;
	struct fb_var_screeninfo vinfo;

	g_fbDispaly = open(DISPLAYDEVICE, O_RDWR);
	if(g_fbDispaly <0)
	{
		fprintf(stderr,"open %s error %s\n",DISPLAYDEVICE, strerror(errno));
		return -1;
	}

	ret = ioctl(g_fbDispaly, FBIOGET_VSCREENINFO, &vinfo);
	if(ret <0)
	{
		fprintf(stderr,"ioctl vscreeninto error:%s\n",strerror(errno));
		close(g_fbDispaly);
		return -2;
	}
	g_tDisplayOpr.height	= vinfo.yres;
	g_tDisplayOpr.width		= vinfo.xres;
	g_tDisplayOpr.pixel		= vinfo.bits_per_pixel;
	g_tDisplayOpr.lineSize	= vinfo.xres*vinfo.bits_per_pixel/8;
	g_fbSzie				= vinfo.yres*vinfo.xres*vinfo.bits_per_pixel/8;

	g_pfbmmap = mmap(NULL, g_fbSzie, PROT_READ|PROT_WRITE, MAP_SHARED, g_fbDispaly, 0);
	if(g_pfbmmap ==MAP_FAILED)
	{
		fprintf(stderr,"mmap fb error:%s\n",strerror(errno));
		close(g_fbDispaly);
		return -3;
	}
	
	return 0;
}

static int fbDevExit(void)
{
	munmap(g_pfbmmap,g_fbSzie);
	close(g_fbDispaly);

	return 0;
}

static int fbClear(unsigned int color)
{
	unsigned int red,green,blue;
	unsigned short bcolor;

	switch(g_tDisplayOpr.pixel)
	{
		case 8:
			memset(g_pfbmmap, color&0xff, g_fbSzie);
			break;

		case 16:
			red 		= (color&0xf80000)>>8;
			green 		= (color&0xfc00)>>5;
			blue 		= (color&0xf8)>>3;
			bcolor 		= red|green|blue;
			memset(g_pfbmmap, bcolor, g_fbSzie);
			break;

		case 32:
			memset(g_pfbmmap, color, g_fbSzie);
			break;

		default:
			fprintf(stderr,"unknown the pixel:%d\r\n",g_tDisplayOpr.pixel);
			return -2;
			break;
	}

	return 0;
}

static int fbClearArea(int xStart,int yStart, int xEnd, int yEnd, unsigned int color)
{
	unsigned int red,green,blue;
	unsigned short bcolor;
	unsigned int bppByte;
	int x,y;

	if(xStart >=g_tDisplayOpr.width || yStart >=g_tDisplayOpr.height)
		return -1;
	
	bppByte = g_tDisplayOpr.pixel/8;
	switch(g_tDisplayOpr.pixel)
	{
		case 8:
			for(y= yStart; y<yEnd; y++)
			{
				for(x = xStart; x<xEnd; x++)
				{
					if(x>=g_tDisplayOpr.width || y>=g_tDisplayOpr.height)
						continue;
					*(unsigned char *)(g_pfbmmap+g_tDisplayOpr.lineSize*y+bppByte*x) = color&0xff;
				}
			}
			break;

		case 16:
			red 		= (color&0xf80000)>>8;
			green 		= (color&0xfc00)>>5;
			blue 		= (color&0xf8)>>3;
			bcolor 		= red|green|blue;
			for(y= yStart; y<yEnd; y++)
			{
				for(x = xStart; x<xEnd; x++)
				{
					if(x>=g_tDisplayOpr.width || y>=g_tDisplayOpr.height)
						continue;
					*(unsigned short *)(g_pfbmmap+g_tDisplayOpr.lineSize*y+bppByte*x) = bcolor;
				}
			}
			break;

		case 32:
			for(y= yStart; y<yEnd; y++)
			{
				for(x = xStart; x<xEnd; x++)
				{
					if(x>=g_tDisplayOpr.width || y>=g_tDisplayOpr.height)
						continue;
					*(unsigned int *)(g_pfbmmap+g_tDisplayOpr.lineSize*y+bppByte*x) = color;
				}
			}
			break;

		default:
			fprintf(stderr,"unknown the pixel:%d\r\n",g_tDisplayOpr.pixel);
			return -2;
			break;
	}	

	return 0;
}

static int fbDrawLine(int xStart,int yStart, int lenth, int blod, unsigned int color)
{
	unsigned int red,green,blue;
	unsigned short bcolor;
	unsigned int bppByte;
	int x,y;
	int xEnd = xStart+lenth;
	int yEnd = yStart+blod;

	if(xStart >=g_tDisplayOpr.width || yStart >=g_tDisplayOpr.height)
		return -1;
	
	bppByte = g_tDisplayOpr.pixel/8;
	switch(g_tDisplayOpr.pixel)
	{
		case 8:
			for(y= yStart; y<yEnd; y++)
			{
				for(x = xStart; x<xEnd; x++)
				{
					if(x>=g_tDisplayOpr.width || y>=g_tDisplayOpr.height)
						continue;
					*(unsigned char *)(g_pfbmmap+g_tDisplayOpr.lineSize*y+bppByte*x) = color&0xff;
				}
			}
			break;

		case 16:
			red 		= (color&0xf80000)>>8;
			green 		= (color&0xfc00)>>5;
			blue 		= (color&0xf8)>>3;
			bcolor 		= red|green|blue;
			for(y= yStart; y<yEnd; y++)
			{
				for(x = xStart; x<xEnd; x++)
				{
					if(x>=g_tDisplayOpr.width || y>=g_tDisplayOpr.height)
						continue;
					*(unsigned short *)(g_pfbmmap+g_tDisplayOpr.lineSize*y+bppByte*x) = bcolor;
				}
			}
			break;

		case 32:
			for(y= yStart; y<yEnd; y++)
			{
				for(x = xStart; x<xEnd; x++)
				{
					if(x>=g_tDisplayOpr.width || y>=g_tDisplayOpr.height)
						continue;
					*(unsigned int *)(g_pfbmmap+g_tDisplayOpr.lineSize*y+bppByte*x) = color;
				}
			}
			break;

		default:
			fprintf(stderr,"unknown the pixel:%d\r\n",g_tDisplayOpr.pixel);
			return -2;
			break;
	}	

	return 0;
}

static int fbShowPixel(int x, int y, unsigned int frontcolor)
{
	unsigned char	*u8_buff	= g_pfbmmap +((y*g_tDisplayOpr.width+x)*g_tDisplayOpr.pixel)/8;
	unsigned short	*u16_buff	= (unsigned short *)u8_buff;
	unsigned int	*u32_buff	= (unsigned int *)u8_buff;
	unsigned int red,green,blue;
	unsigned short color;

	if(x>g_tDisplayOpr.width || y> g_tDisplayOpr.height)
	{
		fprintf(stderr,"the x or y is too large\n");
		return -1;
	}

	switch(g_tDisplayOpr.pixel)
	{
		case 8:
			*u8_buff 	= frontcolor&0xff;
			break;

		case 16:
			red 		= (frontcolor&0xf80000)>>8;
			green 		= (frontcolor&0xfc00)>>5;
			blue 		= (frontcolor&0xf8)>>3;
			color 		= red|green|blue;
			*u16_buff 	= color;
			break;

		case 32:
			*u32_buff 	= frontcolor;
			break;

		default:
			fprintf(stderr,"unknown the pixel:%d\r\n",g_tDisplayOpr.pixel);
			return -2;
			break;
	}

	return 0;
}

static unsigned char * getFbDisplayMmap(void)
{
	return g_pfbmmap;
}

int fbInit(void)
{
	int ret;

	ret = registerDisplayOpr(&g_tDisplayOpr);
	return ret;
}

